iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0
Modern Web

傳承D的意志~ 邁向Django的偉大航道系列 第 15

[Day 15] 實戰篇: 新手必打的怪 使用者註冊

  • 分享至 

  • xImage
  •  

嗨大家好,我是Sean!
寫到今天,整個鐵人賽的系列也到了一半的地方了,
大部分關於Django的架構以及Django rest framework大致上的概念以及簡單操作,都放在那裏了!去尋找吧!(其實就在底下的連結)

好的,總而言之,比較偏理論示範的部分已經告一個段落了!
接下來的部分,可能會偏向針對實戰常見的小專案以及實務中我所遇到並解決的後端問題為主。
有些遇見的實務問題,我想應該也會對很多新接觸Django的朋友很有幫助。

相信對學習coding的大家來說,遇到問題並解決的這個部分,也是coding的一大樂趣嗎(?
https://ithelp.ithome.com.tw/upload/images/20220930/201510964eG7uQeosD.jpg

那麼,就讓我們進入第一個實戰篇的部分,大部分人在學習、熟悉框架的時候,都會學到的使用者註冊。
網頁與使用者幾乎是分不開的兩個部分,比起以往大多是匿名使用的網站,近期很熱門的會員訂閱制,都讓使用者的部分更加重要了。

使用者註冊


在我們的Day06的關聯資料篇,曾經有提到過關於使用者的部分,其中一個原因是由於常用的一對一關係(OneToOneField),就是使用在原生auth.User身上。

這裡我們就來實作這個方法,讓大家看看運用原生table而做成的UserProfile會有甚麼效果。

原生的User model


首先,我們先來Django的official document看看,它裡面有包含哪些內容。
https://docs.djangoproject.com/en/4.1/ref/contrib/auth/#django.contrib.auth.models.User

光是基本的原生model就已經有:

  1. username
  2. first_name
  3. last_name
  4. email
  5. password ...等等常見的欄位了。

在我們建立model之前,先參考有哪些欄位是可以直接使用的,避免重複了相同的欄位。
以上的這些欄位內容,其實在我們進入admin的管理者頁面,也可以看的到。
https://ithelp.ithome.com.tw/upload/images/20220930/20151096tEyjmYeK3Z.png

在底下也有原生的Group model可以做使用!

建立在User之上: UserProfile


接下來我們來擴充User的model,用UserProfile這個model來做一對一的關聯。

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    phone = models.CharField(max_length=10, blank=True, null=True)
    organization = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
        return self.user.username

Register api


進行了migration以後,我們就可以來寫serializer, view的部分了。
首先我們必須寫serializer,依靠他的驗證以及一同建立User以及UserProfile的部分。

serializer

from django.contrib.auth.models import User
from .models import UserProfile
from rest_framework.validators import UniqueValidator
from django.contrib.auth.password_validation import validate_password

class RegisterSerializer(serializers.ModelSerializer):
    username = serializers.CharField(
        required=True,
        validators=[UniqueValidator(queryset=User.objects.all())],
    )
    password = serializers.CharField(write_only=True, required=True, validators=[validate_password],
                                     style={'input_type': 'password'})
    password2 = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'})
    email = serializers.EmailField(write_only=True, required=True)
    phone = serializers.CharField(min_length=8, max_length=10, write_only=True)
    organization = serializers.CharField(max_length=100)

    class Meta:
        model = User
        fields = ('username', 'email', 'password', 'password2', 'phone', 'organization')

    def validate(self, attrs):
        if attrs['password'] != attrs['password2']:
            raise serializers.ValidationError({"password": "Password fields didn't match."})

        return attrs

    def create(self, validated_data):
        user = User.objects.create(
            username=validated_data['username'],
            email=validated_data['email'],
            password=validated_data['password'],
        )

        userprofile = UserProfile.objects.create(
            user=user,
            phone=validated_data['phone'],
            organization=validated_data['organization'],
        )

        user.set_password(validated_data['password'])
        user.save()

        return user

讓我們來分析一下,以上做了哪些事:

  1. 首先,我們定義了serializer的欄位,並且在欄位中寫了他們的條件驗證,其中比較特別的是username跟password的部分
  • username的部分,我們用到了rest framework的UniqueValidator,來確認他是唯一
  • password則有兩個,原因是必須要給使用者確認一次密碼,用validate_password來驗證兩次的密碼是否相同,另外再加上了input_type,讓我們輸入的時候密碼會加以屏蔽。

2.model的部分主要還是使用User為主,UserProfile是擴充包,在fields裡,仍必須把我們UserProfile裡的欄位寫進去。

3.我們使用ORM的方法來create model instance,一方面重新定義create,另一方面我們也同時create UserProfile的 instance,並對應我們創建的User instance。

4.最後,儲存密碼並且return user

view

from rest_framework import generics
from .serializers import RegisterSerializer

class RegisterAPIView(generics.GenericAPIView):
    serializer_class = RegisterSerializer

    def post(self, request):
        serializer = self.serializer_class(data = request.data)
        if serializer.is_valid():
            serializer.save()

            return Response({
                'message': 'Account has been created successfully'
                },
                status=status.HTTP_201_CREATED
            )

        return Response({
            'message': 'The input content is invalid'
            },
            status=status.HTTP_400_BAD_REQUEST
        )

view的部分,我們用GenericAPIView來做,他是一個可以自定義的api view。
寫完必要的serializer_class以後,我們定義使用post method的功能:

讓輸入的值成為serializer可以使用data=request.data,

接著,在save之前,我們得先用is_valid()的功能來驗證serializer,

最後在Response的地方回傳訊息以及status,一支用來註冊的後端api就大功告成了!
完成的畫面會像下面的畫面這樣!

https://ithelp.ithome.com.tw/upload/images/20220930/201510967tn7ZGQ3SK.png

那麼,今天的文章就到此結束!感謝大家的收看!
我是Sean,你各位海上的人,我們明天見!
https://ithelp.ithome.com.tw/upload/images/20220930/20151096BkC4eoa5zl.jpg


上一篇
[Day 14] DRF 再見你最後一面!
下一篇
[Day 16] 實戰篇 : 新手必打的怪 使用者登入與登出
系列文
傳承D的意志~ 邁向Django的偉大航道30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言